#include <iostream>
#if defined(_WIN32)
#include <Windows.h>
#include <process.h>
#else
#include <unistd.h>
#endif
#include "mgnotify.h"
#include "center.hpp"

using namespace std;

mgnotify* mgnotify::self = nullptr;
mutex mgnotify::qmutex;

mgnotify* mgnotify::getInstance()
{
    if(self == nullptr) {
        lock_guard<mutex> locker(qmutex);
        if(self == nullptr)
            self = new mgnotify();
    }
    return self;
}

mgnotify::mgnotify() : mthread(&mgnotify::run, this)
{
    isrunning = true;
#if defined(_WIN32)
#else
    mthread.detach();
#endif
}

mgnotify::~mgnotify()
{
    isrunning = false;
#if defined(_WIN32)
    if(mthread.joinable())
        mthread.join();
#endif
    if(self != nullptr) {
        delete self;
        self = nullptr;
    }
}


void mgnotify::attach(IHandle* handle)
{
    if(handle == nullptr) {
#if defined(_WIN32)
        OutputDebugStringA("invalid handle");
#else
        perror("invalid handle");
#endif
        return;
    }

    string t = handle->getType();
    auto p = mhandle[t];
    if(p == nullptr) {
        p = new LHandle;
        p->push_back(handle);
        mhandle[t] = p;
    }
    else {
        for (LHandle::iterator it = p->begin(); it != p->end(); ++it) {
            if(*it == handle) {
                return;
            }
        }
        p->push_back(handle);
    }
}

void mgnotify::detach(IHandle* handle)
{
    if(handle == nullptr) {
#if defined(_WIN32)
        OutputDebugStringA("invalid handle");
#else
        perror("invalid handle");
#endif
        return;
    }

    string t = handle->getType();
    auto p = mhandle[t];
    for (LHandle::iterator it = p->begin(); it != p->end(); ++it) {
        if(*it == handle)
        {
            p->remove(handle);
            break;
        }
    }
}

bool mgnotify::sendMessage(const IMessage* msg)
{    
    auto t = typeid(*msg).name();
    auto p = mhandle[t];
    if(p == nullptr) {
#if defined(_WIN32)
        OutputDebugStringA("invalid sender");
#else
        perror("invalid sender");
#endif
        return false;
    }
    if(p->empty()) {
#if defined(_WIN32)
        OutputDebugStringA("empty receivers");
#else
        perror("empty receivers");
#endif
        return false;
    }
    for(auto it = p->begin(); it != p->end(); ++it) {
        msg->send(*it);
    }
    return true;
}

void mgnotify::pushMessage(IMessage* msg, MessagePriority priority)
{
    {
        lock_guard<mutex> locker(tmutex);
        if(msg == nullptr) {
#if defined(_WIN32)
            OutputDebugStringA("sender is nullptr");
#else
            perror("sender is nullptr");
#endif
            return;
        }
        switch(priority) {
        case MP_COMMON:
            msglist.push_back(msg);
            break;
        case MP_EMERGENCY:
            msglist.push_front(msg);
            break;
        default:
            msglist.push_back(msg);
            break;
        }
    }
    tcvar.notify_one();
}

void mgnotify::run()
{
    while(isrunning) {
        IMessage* msg = nullptr;
        {
            unique_lock<mutex> locker(tmutex);
            if(msglist.empty())
                tcvar.wait(locker);
        }
        {
            lock_guard<mutex> locker(tmutex);
            if(msglist.empty())
                continue;
            msg = msglist.front();
            msglist.pop_front();
        }

        auto t = typeid(*msg).name();
        auto p = mhandle[t];
        if(p == nullptr) {
#if defined(_WIN32)
            OutputDebugStringA("invalid sender");
#else
            perror("invalid sender");
#endif
            continue;
        }
        if(p->empty()) {
#if defined(_WIN32)
            OutputDebugStringA("empty receivers");
#else
            perror("empty receivers");
#endif
            continue;
        }
        for(auto it = p->begin(); it != p->end(); ++it) {
            msg->send(*it);
        }
        
        delete msg;
        msg = nullptr;
    }
}


int mgnotify::senderCount() const
{
    return mhandle.size();
}

std::list<std::string> mgnotify::senders()
{
    list<string> t;
    for (auto it = mhandle.begin(); it != mhandle.end(); ++it) {
        t.push_back(it->first);
    }
    return t;
}

int mgnotify::receiverCount(std::string sender)
{
    auto p = mhandle[sender];
    return p == nullptr ? 0 : p->size();
}